/* Author	: Manish YM
 * Date		: 04/08/2009
 * Description	: Using linked lists in kernel
 */


/*
 * This example shows how to defer work using linked lists
 */


static struct _mydrv_wq
{
    struct list_head mydrv_worklist ; /* This is the head of linked list*/
    spinlock_t lock ;
    wait_queue_head_t todo ;
} mydrv_wq ;

static struct _mydrv_work
{
    struct list_head mydrv_workitem ;
    void (*worker_func) (void *);
    void *worker_data ;
} mydrv_work ;



/*
 * Initializing the work queue
 */
static int __init mydrv_init (void)
{
    spin_lock_init ( &mydrv_wq.lock) ;
    /* Initialize the spinlock */
    init_waitqueue_head (&mydrv_wq.todo) ;
    /* This is kind of binary semaphore. 
     */
    
    INIT_LIST_HEAD (&mydrv_wq.mydrv_worklist) ;

    kernel_thread (mydrv_worker, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND |CLONE_SIGCHLD);
 
    return 0;
}

static int submit_work ( void (*func) (void *data), void *data )
{
    /*
     * Take function and args, allocate a work structure, add work structure to worklist
     * wake up work thread
     * exit
     */
    struct _mydrv_work *mydrv_work ;

    mydrv_work = kmalloc ( sizeof (struct _mydrv_work) , GFP_ATOMIC ) ;
    /*Check return*/
    mydrv_work->worker_func = func ;
    mydrv_work->worker_data = data ;

    spin_lock(&mydrv_wq.lock);
    list_add_tail ( &mydrv_work -> mydrv_workitem, &mydrv_wq.mydrv_worklist) ;
    wake_up (&mydrv_wq.todo) ;
    spin_unlock (&mydrv_wq.lock) ;
    return 0;
}
	

	
    
